﻿#include "channelitemview.h"
#include <QContextMenuEvent>
#include <QMenu>
#include "channelchatroom.h"
#include "channelitemmodel.h"
#include "contactuser.h"
#include "clientcallback.h"
#include "globaltools.h"
#include "litedb.h"
#include "p2pchatroom.h"
#include "protohash.h"
#include "servertimesync.h"
#include "uitools.h"

ChannelItemView::ChannelItemView(QWidget *parent) : QListView(parent), myuid(ClientCallback::instance()->get_my_id())
{
    channelModel = new ChannelItemModel(this);
    setModel(channelModel);
    setItemDelegate(new ChannelItemDelegate(this));
    //因为hidden用的是persistedIndex，move时貌似没有影响
    //connect(channelModel, &ChannelItemModel::rowsMoved, this, &ChannelItemView::checkAllRowHidden);
    connect(channelModel, &ChannelItemModel::modelReset, this, &ChannelItemView::checkAllRowHidden);
}

bool ChannelItemView::hasChannel(qint64 rid) const
{
    return map_channel.contains(rid);
}

const ChannelInfo *ChannelItemView::constFindChannel(qint64 rid) const
{
    auto it = map_channel.constFind(rid);
    if(it == map_channel.constEnd())
        return Q_NULLPTR;
    return it.operator->();
}

const P2PChanneInfo *ChannelItemView::constFindP2pChannel(qint64 uid) const
{
    auto it = map_p2pchannel.constFind(uid);
    if(it == map_p2pchannel.constEnd())
        return Q_NULLPTR;
    return it.operator->();
}

const BaseChannelInfo *ChannelItemView::findAllChannel(qint64 uniqueId) const
{
    auto info = BaseChannelInfo::fromUniqueId(uniqueId);
    if(info.first)
        return constFindP2pChannel(info.second);
    else
        return constFindChannel(info.second);
}

ChannelInfo *ChannelItemView::findChannel(qint64 rid)
{
    auto it = map_channel.find(rid);
    if(it == map_channel.end())
        return Q_NULLPTR;
    return it.operator->();
}

P2PChanneInfo *ChannelItemView::findP2pChannel(qint64 uid)
{
    auto it = map_p2pchannel.find(uid);
    if(it == map_p2pchannel.end())
        return Q_NULLPTR;
    return it.operator->();
}

void ChannelItemView::handle_login_ack(qint64 rid, QVariantHash &msg)
{
    int code = getParam(msg, cmd_param_code).toInt();
    auto it = map_channel.find(rid);
    if(code == error_none)
    {
        //登陆成功
        if(it == map_channel.end())
        {
            //新建
            it = map_channel.insert(rid, ChannelInfo(rid));
            channelModel->add_channel(it.operator->());
        }
        handle_basic_info(rid, msg);
    }
    else if(it != map_channel.end())
    {
        //登陆失败
    }
}

//audio/video state: 0:无 1:有 -1:不检查
static QSet<qint64> getListFromMicState(qint8 audioState, qint8 videoState, const QList<QVariantHash> &mics)
{
    QSet<qint64> list;
    for(const auto& mic : mics)
    {
        qint64 uid = getParam(mic, cmd_param_uid).toLongLong();
        if(uid <= 0)
            continue;
        if(audioState >= 0 && audioState != (getParam(mic, cmd_param_audio_id).toInt() > 0))
            continue;
        if(videoState >= 0 && videoState != (getParam(mic, cmd_param_video_id).toInt() > 0))
            continue;
        list.insert(uid);
    }
    return list;
}

static QSet<qint64> getListFromApplyState(const QList<QVariantHash> &applies)
{
    QSet<qint64> list;
    for(const auto& apply : applies)
    {
        qint64 uid = getParam(apply, cmd_param_uid).toLongLong();
        if(uid > 0)
            list.insert(uid);
    }
    return list;
}

ChatRoom *ChannelItemView::handle_open(qint64 uniqueId)
{
    if(BaseChannelInfo::isP2PFromUniqueId(uniqueId))
    {
        //p2p
        auto uid = BaseChannelInfo::idFromUniqueId(uniqueId);
        auto it = findP2pChannel(uid);
        if(!it)
        {
            it = createP2pChannel(uid);
            if(!it)
                return Q_NULLPTR;
            channelModel->add_channel(it);
        }
        auto room = new P2PChatRoom(*it);
        room->handle_msg_list(it->get_msg_list());
        //1.set read
        auto mids = it->mark_read();
        if(mids.size() > 0)
            channelModel->maybeReordered(it);
        //2.mark read
        P2PChanneInfo::call_mark_read_all(uid, mids);
        return room;
    }
    else
    {
        auto rid = BaseChannelInfo::idFromUniqueId(uniqueId);
        auto it = findChannel(rid);
        if(!it)
            return Q_NULLPTR;

        auto room = new ChannelChatRoom(*it);
        room->handle_msg_list(it->get_msg_list());
        room->handle_mic_state(it->mic_list, it->apply_list, true);
        QSet<qint64> empty;
        room->handle_mic_change(empty, getListFromMicState(1, 0, it->mic_list),
                                empty, getListFromMicState(-1, 1, it->mic_list),
                                empty, getListFromApplyState(it->apply_list));
        //set video list
        ClientCallback::instance()->set_video_list(QSet<qint64>({rid}));        
        //1.set read
        auto mids = it->mark_read();
        if(mids.size() > 0)
            channelModel->maybeReordered(it);
        //2.mark read
        ChannelInfo::call_mark_read_all(rid, mids);
        return room;
    }
}

void ChannelItemView::handle_join_or_exit(int cmd_type, qint64 rid, QVariantHash &msg)
{
    if(auto it = constFindChannel(rid)) {
        qint64 uid = getParam(msg, cmd_param_uid).toLongLong();
        if(cmd_type == cmd_type_ntfy_exit && uid == ClientCallback::instance()->get_my_id())
        {
            //我从房间掉线
        }
        else if(uid > 0) {
            //
        }
    }
}

void ChannelItemView::handle_channel_list(const QList<QVariantHash> &channel_list)
{
    QList<const BaseChannelInfo *> const_channels;
    const_channels.reserve(channel_list.size() + map_p2pchannel.size());

    QMap<qint64, ChannelInfo> channels;
    int sameCount = 0;
    for(auto channel_info : channel_list)
    {
        qint64 rid = getParam(channel_info, cmd_param_rid).toLongLong();
        if (rid <= 0)
            continue;
        ChannelInfo& channelInfo = channels[rid];

        const QString name = getParam(channel_info, cmd_param_name).toString();
        auto it = map_channel.constFind(rid);
        if(it != map_channel.constEnd())
        {
            channelInfo = *it;
            if(name == channelInfo.rname)
                ++sameCount;
            else
                channelInfo.rname = name;
        }
        else
        {
            channelInfo.rid = rid;
            channelInfo.rname = name;
        }

        const_channels.append(&channelInfo);
    }
    if(channels.size() == map_channel.size() && channels.size() == sameCount)
        return;

    //加入p2p
    for (auto it = map_p2pchannel.constBegin(); it != map_p2pchannel.constEnd(); it++)
        const_channels.append(it.operator->());

    channelModel->set_channel_list(const_channels);
    map_channel.swap(channels);
}

void ChannelItemView::handle_channel_msg(qint64 rid, QVariantHash &msg, bool isChannelOpen)
{
    auto it = map_channel.find(rid);
    if(it != map_channel.end()) {
        QByteArray mid = getParam(msg, cmd_param_mid).toByteArray();
        if(!mid.isEmpty())
        {
            const auto last = it->last_msg_time();
            const auto unread = it->unread_count;
            setMsgDirection(msg);
            it->add_msg_list(mid, msg, isChannelOpen);
            if(last != it->last_msg_time() || unread != it->unread_count)
                channelModel->maybeReordered(it.operator->());
        }
    }
}

void ChannelItemView::handle_channel_msg_list(const qint64 &rid, QList<QVariantHash> &msg_list)
{
    auto it = map_channel.find(rid);
    if(it != map_channel.end()) {
        const auto last = it->last_msg_time();
        const auto unread = it->unread_count;
        for(auto &msg : msg_list)
        {
            QByteArray mid = getParam(msg, cmd_param_mid).toByteArray();
            if(mid.isEmpty())
                continue;
            setMsgDirection(msg);
            it->add_msg_list(mid, msg, false);
        }
        if(last != it->last_msg_time() || unread != it->unread_count)
            channelModel->maybeReordered(it.operator->());
    }
}

static void removeIntersect(QSet<qint64> &old_list, QSet<qint64> &new_list)
{
    auto it = old_list.begin();
    while (it != old_list.end()) {
        auto find = new_list.find(*it);
        if(find == new_list.end())
        {
            //旧的里有，新的里没有，保留
            it++;
        }
        else
        {
            //都有，两边都删除
            new_list.erase(find);
            it = old_list.erase(it);
        }
    }
}

static void createSystemMsg(ChannelInfo& channel, qint64 uid, const QString &text, QList<QVariantHash> &msgList)
{
    QString msgText = text;
    if(uid > 0)
    {
        QString name;
        for(const auto &member : channel.member_list)
        {
            if(getParam(member, cmd_param_uid).toLongLong() == uid)
            {
                name = getParam(member, cmd_param_name).toString();
                break;
            }
        }
        if(name.isEmpty())
            name = QString::number(uid);
        msgText.prepend(name + ' ');
    }
    auto time = ServerTimeSync::instance()->getServerTime();
    auto mid = create_mid(time, msgText);

    auto &msg = channel.create_msg(time, mid);

    setParam(msg, cmd_param_msg_type, msg_type_notify);
    setParam(msg, cmd_param_rid, channel.rid);
    setParam(msg, cmd_param_snd_id, 10000);
    setParam(msg, cmd_param_snd_name, QObject::trECSUTF8("系统"));
    //setParam(msg, cmd_param_state, msg_state_readed);
    setParam(msg, cmd_param_msg, msgText);
    setParam(msg, cmd_param_mid, mid);
    msgList.append(msg);
}

QList<QVariantHash> ChannelItemView::handle_mic_state(qint64 rid, QVariantHash &msg)
{
    if(!hasParam(msg, cmd_param_mic_ver))
        return QList<QVariantHash>();
    auto it = map_channel.find(rid);
    if(it != map_channel.end()) {
        auto old_video_list = getListFromMicState(-1, 1, it->mic_list);
        auto old_audio_list = getListFromMicState(1, 0, it->mic_list);
        auto old_apply_list = getListFromApplyState(it->apply_list);

        it->mic_list = ProtoArray::decodeArray(getParam(msg, cmd_param_mic_list).toByteArray());
        it->apply_list = ProtoArray::decodeArray(getParam(msg, cmd_param_apply_list).toByteArray());

        auto new_video_list = getListFromMicState(-1, 1, it->mic_list);
        auto new_audio_list = getListFromMicState(1, 0, it->mic_list);
        auto new_apply_list = getListFromApplyState(it->apply_list);

        removeIntersect(old_audio_list, new_audio_list);
        removeIntersect(old_video_list, new_video_list);
        removeIntersect(old_apply_list, new_apply_list);

        QList<QVariantHash> append_msg_list;

        for(auto audio_over : old_audio_list)
        {
            createSystemMsg(*it, audio_over, QObject::trECSUTF8("结束对讲"), append_msg_list);
        }
        for(auto audio_start : new_audio_list)
        {
            createSystemMsg(*it, audio_start, QObject::trECSUTF8("开始对讲"), append_msg_list);
        }
        for(auto video_over : old_video_list)
        {
            createSystemMsg(*it, video_over, QObject::trECSUTF8("结束视频"), append_msg_list);
        }
        for(auto video_start : new_video_list)
        {
            createSystemMsg(*it, video_start, QObject::trECSUTF8("分享视频"), append_msg_list);
        }

        if(it->mic_changes.size() == 6)
        {
            it->mic_changes[0].swap(old_audio_list);
            it->mic_changes[1].swap(new_audio_list);
            it->mic_changes[2].swap(old_video_list);
            it->mic_changes[3].swap(new_video_list);
            it->mic_changes[4].swap(old_apply_list);
            it->mic_changes[5].swap(new_apply_list);
        }
        else
        {
            QVector<QSet<qint64>> mic_changes(6);
            mic_changes[0].swap(old_audio_list);
            mic_changes[1].swap(new_audio_list);
            mic_changes[2].swap(old_video_list);
            mic_changes[3].swap(new_video_list);
            mic_changes[4].swap(old_apply_list);
            mic_changes[5].swap(new_apply_list);
            it->mic_changes.swap(mic_changes);
        }
        if(append_msg_list.size() > 0)
        {
            channelModel->maybeReordered(it.operator->());
        }
        return append_msg_list;
    }
    return QList<QVariantHash>();
}

static void setContactName(std::function<const ContactUser *(qint64)> &getContactUser, BaseChannelInfo &channel)
{
    for(auto &member: channel.member_list)
    {
        if(getParam(member, cmd_param_name).toString().isEmpty())
        {
            if(auto user = getContactUser(getParam(member, cmd_param_uid).toLongLong()))
            {
                if(!user->name.isEmpty())
                    setParam(member, cmd_param_name, user->name);
            }
        }
    }
}

void ChannelItemView::handle_basic_info(qint64 rid, QVariantHash &msg)
{
    auto it = map_channel.find(rid);
    if(it == map_channel.end())
        return;
    if (hasParam(msg, cmd_param_basicinfo_ver)) {
        it->member_list = ProtoArray::decodeArray(getParam(msg, cmd_param_member_list).toByteArray());
        if(getContactUser)
            setContactName(getContactUser, *it);
        it->room_info = ProtoHash::decodeHash(getParam(msg, cmd_param_room_info).toByteArray());
        bool changed = false;
        if(hasParam(it->room_info, cmd_param_style))
        {
            int style = getParam(it->room_info, cmd_param_style).toInt();
            if(style != it->rstyle)
            {
                it->rstyle = style;
                changed = true;
            }
        }
        if(hasParam(it->room_info, cmd_param_name))
        {
            QString name = getParam(it->room_info, cmd_param_name).toString();
            if(name != it->rname)
            {
                it->rname = name;
                changed = true;
            }
        }
        if(changed)
            channelModel->emitDataChanged(it.operator->());
    }
}

void ChannelItemView::handle_contact_list()
{
    if(getContactUser)
    {
        for(auto &channel: map_channel)
            setContactName(getContactUser, channel);
        for(auto &channel: map_p2pchannel)
            setContactName(getContactUser, channel);
    }
}

QList<QVariantHash> ChannelItemView::handle_mic_ack(qint64 rid, int code, const QString &action)
{
    auto it = map_channel.find(rid);
    if(it != map_channel.end()) {
        QList<QVariantHash> append_msg_list;
        if (code == error_code_limited_resource) {
            createSystemMsg(*it, 0, action+QObject::trECSUTF8("对讲或者视频话权失败！")+QObject::trECSUTF8("您所在频道没有足够的话权资源"), append_msg_list);
        }
        else if (code == error_code_permission_deny) {
            createSystemMsg(*it, 0, action+QObject::trECSUTF8("对讲或者视频话权失败！")+QObject::trECSUTF8("您没有权限进行操作"), append_msg_list);
        }
        else if (code == error_code_room_not_exist) {
            createSystemMsg(*it, 0, action+QObject::trECSUTF8("对讲或者视频话权失败！")+QObject::trECSUTF8("您所在的频道不存在或者尚未准备好"), append_msg_list);
        }
        else if (code == error_code_blacklist_blocked) {
            createSystemMsg(*it, 0, action+QObject::trECSUTF8("对讲或者视频话权失败！")+QObject::trECSUTF8("您进入了系统黑名单"), append_msg_list);
        }
        else if (code == error_code_local_room_not_exist) {
            createSystemMsg(*it, 0, action+QObject::trECSUTF8("对讲或者视频话权失败！")+QObject::trECSUTF8("该频道不在您的可操作频道列表中"), append_msg_list);
        }
        else if (code == error_code_network_timeout) {
            createSystemMsg(*it, 0, action+QObject::trECSUTF8("对讲或者视频话权失败！")+QObject::trECSUTF8("网络错误，终端网络信号信号弱或无网络"), append_msg_list);
        }
        else if (code == error_code_invalid_timestamp) {
            createSystemMsg(*it, 0, action+QObject::trECSUTF8("对讲或者视频话权失败！")+QObject::trECSUTF8("时间错误，指令过期"), append_msg_list);
        }
        else if (code != error_none) {
            createSystemMsg(*it, 0, action+QObject::trECSUTF8("对讲或者视频话权失败！")+QObject::trECSUTF8("其他错误:")+QString::number(code), append_msg_list);
        }
        else
            return append_msg_list;
        channelModel->maybeReordered(it.operator->());
        return append_msg_list;
    }
    return QList<QVariantHash>();
}

void ChannelItemView::setMsgDirection(QVariantHash &msg)
{
    setParam(msg, cmd_param_direction, (getParam(msg, cmd_param_snd_id).toLongLong() == myuid) ? msg_dir_send : msg_dir_recv);
}

bool ChannelItemView::preprocess_p2p_msg(const qint64 &uid, int type, QVariantHash &msg)
{
    bool isVideo = false, isOn = false;
    if(type == msg_type_dispatch_start)
    {
        auto curtime = ServerTimeSync::instance()->getServerTime();
        auto msgtime = getParam(msg, cmd_param_msg_time).toLongLong();
        if (curtime - msgtime > 15 * 1000) {
            return false;
        }
        isVideo = true;
        isOn = true;
        log_debug() << "msg_type_dispatch_start" << endl;
    }
    else if(type == msg_type_dispatch_end)
    {
        auto curtime = ServerTimeSync::instance()->getServerTime();
        auto msgtime = getParam(msg, cmd_param_msg_time).toLongLong();
        if (curtime - msgtime > 15 * 1000) {
            return false;
        }
        isVideo = true;
        isOn = false;
        log_debug() << "msg_type_dispatch_end" << endl;
    }
    else if(type == msg_type_notify)
    {
        QString content = getParam(msg, cmd_param_msg).toString();
        if(content == P2PChanneInfo::start_talk)
        {
            isOn = true;
            log_debug() << "msg_type_notify : start_talk" << endl;
        }
        else if(content == P2PChanneInfo::end_talk)
        {
            log_debug() << "msg_type_notify : end_talk" << endl;
        }
        else
            return false;
    }
    else if(type == msg_type_msg_readed)
    {
        //对方已读
        auto mids = getParam(msg, cmd_param_msg).toByteArray().split(',');
        auto channel = findP2pChannel(uid);
        auto client = ClientCallback::instance();
        foreach (const auto &mid, mids) {
            //1.set peer read
            if(channel)
                channel->mark_peer_read(mid);
            //2.mark peer read
            client->im_mark_peer_read(uid, mid);
        }
        return true;
    }
    else
    {
        //不继续往后面执行的
        //但是跳出后外面还是要往后执行
        return true;
    }

    if(auto channel = findP2pChannel(uid))
    {
        if(isVideo)
        {
            if(channel->hasVideo != isOn)
            {
                channel->hasVideo = isOn;
            }
        }
        else
        {
            if(channel->hasAudio != isOn)
            {
                channel->hasAudio = isOn;
            }
        }
    }
    return true;
}

bool ChannelItemView::handle_p2p_media(const qint64 &uid, bool on)
{
    P2PChanneInfo *info = Q_NULLPTR;
    auto it = map_p2pchannel.find(uid);
    if(it != map_p2pchannel.end())
        info = it.operator->();
    else if((info = createP2pChannel(uid)))
        channelModel->add_channel(info);
    if(info && info->hasVideo != on)
    {
        info->hasVideo = on;
        return true;
    }
    return false;;
}

void ChannelItemView::handle_p2p_msg(const qint64 &uid, QVariantHash &msg, bool isRead)
{
    QByteArray mid = getParam(msg, cmd_param_mid).toByteArray();
    if(mid.isEmpty())
        return;
    if(uid == 10000)    //TODO:系统消息
        return;
    if(!hasParam(msg, cmd_param_direction))
        setMsgDirection(msg);
    auto it = map_p2pchannel.find(uid);
    if(it != map_p2pchannel.end())
    {
        const auto last = it->last_msg_time();
        const auto unread = it->unread_count;
        it->add_msg_list(mid, msg, isRead);
        if(last != it->last_msg_time() || unread != it->unread_count)
            channelModel->maybeReordered(it.operator->());
    }
    else if(auto info = createP2pChannel(uid))
    {
        info->add_msg_list(mid, msg, isRead);
        channelModel->add_channel(info);
    }
}

void ChannelItemView::handle_p2p_msg_list(const qint64 &uid, QList<QVariantHash> &msg_list)
{
    if(uid == 10000)    //TODO:系统消息
        return;
    auto it = map_p2pchannel.find(uid);
    if(it != map_p2pchannel.end())
    {
        const auto last = it->last_msg_time();
        const auto unread = it->unread_count;
        for(auto &msg : msg_list)
        {
            QByteArray mid = getParam(msg, cmd_param_mid).toByteArray();
            if(mid.isEmpty())
                continue;
            it->add_msg_list(mid, msg, false);
        }
        if(last != it->last_msg_time() || unread != it->unread_count)
            channelModel->maybeReordered(it.operator->());
    }
    else if(auto info = createP2pChannel(uid))
    {
        for(auto &msg : msg_list)
        {
            QByteArray mid = getParam(msg, cmd_param_mid).toByteArray();
            if(mid.isEmpty())
                continue;
            info->add_msg_list(mid, msg, false);
        }
        channelModel->add_channel(info);
    }
}

void ChannelItemView::delete_room_message(qint64 rid)
{
    auto it = map_channel.find(rid);
    if(it != map_channel.end()) {
       auto last = it->last_msg();
       it->clear_msg_list();
       if(!last.isEmpty())
       {
           channelModel->maybeReordered(it.operator->());
       }
    }
}

void ChannelItemView::set_room_mute_list()
{
    QByteArrayList rids;
    for(auto it = map_channel.constBegin(); it != map_channel.constEnd(); it++)
    {
        if(it->mute)
            rids << QByteArray::number(it->rid);
    }
    ClientCallback::instance()->set_room_mute_list(rids);
}

void ChannelItemView::set_room_mute(qint64 rid, bool mute)
{
    auto it = map_channel.find(rid);
    if(it != map_channel.end() && it->mute != mute) {
       it->mute = mute;
       set_room_mute_list();
       channelModel->emitDataChanged(it.operator->());
    }
}

void ChannelItemView::delete_p2p_message(qint64 uid)
{
    auto it = map_p2pchannel.find(uid);
    if(it != map_p2pchannel.end()) {
       auto last = it->last_msg();
       it->clear_msg_list();
       if(!last.isEmpty())
       {
           channelModel->maybeReordered(it.operator->());
       }
    }
}

void ChannelItemView::set_user_mute_list()
{
    QByteArrayList uids;
    for(auto it = map_p2pchannel.constBegin(); it != map_p2pchannel.constEnd(); it++)
    {
        if(it->mute)
            uids << QByteArray::number(it->uid);
    }
    ClientCallback::instance()->set_user_mute_list(uids);
}

void ChannelItemView::set_user_mute(qint64 uid, bool mute)
{
    auto it = map_p2pchannel.find(uid);
    if(it != map_p2pchannel.end() && it->mute != mute) {
       it->mute = mute;
       set_user_mute_list();
       channelModel->emitDataChanged(it.operator->());
    }
}

void ChannelItemView::set_mute_all(bool mute)
{
    QList<const BaseChannelInfo*> channels;
    for(auto it = map_channel.begin(); it != map_channel.end(); it++)
    {
        if(it->mute != mute)
        {
            it->mute = mute;
            channels.append(it.operator->());
        }
    }
    if(!channels.isEmpty())
    {
        set_room_mute_list();
        channelModel->emitDataChanged(channels);
    }
}

void ChannelItemView::handle_search(const QString &keyword)
{
    if(keyword.compare(searchword, Qt::CaseInsensitive) == 0)
        return;
    searchword = keyword;
    checkAllRowHidden();
}

void ChannelItemView::contextMenuEvent(QContextMenuEvent *e)
{
    e->accept();
    auto index = indexAt(e->pos());
    auto menu = new QMenu(this);
    menu->setAttribute(Qt::WA_DeleteOnClose);
    bool hasAction = false;
    if(index.isValid())
    {
        if(auto channel = BaseChannelInfo::fromInternalId(index.internalId()))
        {
            auto uniqueId = channel->uniqueId();
            bool mute = !channel->mute;
            auto action_empty = menu->addAction(QObject::trECSUTF8("清空对话"));
            connect(action_empty, &QAction::triggered, this, [this, uniqueId](){
                emit deleteChannelMessage(uniqueId);
            });
            hasAction = true;
            auto id = channel->id();
            if(channel->isP2P())
            {
                //p2p
                auto action_mute = menu->addAction(mute? QObject::trECSUTF8("静音") : QObject::trECSUTF8("取消静音"));
                connect(action_mute, &QAction::triggered, this, [this, mute, id](){
                    set_user_mute(id, mute);
                });
            }
            else
            {
                //room
                auto action_mute = menu->addAction(mute? QObject::trECSUTF8("静音") : QObject::trECSUTF8("取消静音"));
                connect(action_mute, &QAction::triggered, this, [this, mute, id](){
                    set_room_mute(id, mute);
                });
            }
        }
    }
    if(hasAction)
        menu->popup(e->globalPos());
    else
        menu->deleteLater();
}

void ChannelItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
    QListView::dataChanged(topLeft, bottomRight, roles);
    if(!topLeft.parent().isValid() && !bottomRight.parent().isValid())
    {
        checkRowHidden(topLeft.row(), bottomRight.row(), searchword);
    }
}

void ChannelItemView::rowsInserted(const QModelIndex &parent, int start, int end)
{
    QListView::rowsInserted(parent, start, end);
    if(!parent.isValid())
        checkRowHidden(start, end, searchword);
}

void ChannelItemView::checkAllRowHidden()
{
    int count = channelModel->rowCount(QModelIndex());
    if(count > 0)
        checkRowHidden(0, count - 1, searchword);
}

void ChannelItemView::checkRowHidden(int start, int end, const QString &keyword)
{
    for(int row = start; row <= end; row++)
    {
        auto hidden = isRowHidden(row);
        if(hidden != channelModel->isChannelHidden(row, keyword))
            setRowHidden(row, !hidden);
    }
}

P2PChanneInfo *ChannelItemView::createP2pChannel(qint64 uid)
{
    if(getContactUser)
    {
        auto & info = map_p2pchannel[uid];
        info.uid = uid;
        info.hasAudio = info.hasVideo = false;
        if(auto user = getContactUser(uid))
        {
            info.name = user->display();
            info.icon = user->decoration(48, 48, font());
        }
        else
        {
            info.name = QString::number(uid);
            info.icon = ContactUser::decoration(uid, info.name, 48, 48, font());
        }
        QVariantHash hash;
        //我
        auto myid = ClientCallback::instance()->get_my_id();
        setParam(hash, cmd_param_uid, myid);
        if(auto user = getContactUser(myid))
            setParam(hash, cmd_param_name, user->name);
        info.member_list << hash;
        setParam(hash, cmd_param_uid, uid);
        setParam(hash, cmd_param_name, info.name);
        info.member_list << hash;
        return &info;
    }
    return Q_NULLPTR;
}
